/**
 * 19.5.28：增加着色器aspect相关代码，根据宽高比计算。
 * 20220304:[require] to [import]...jordia
 */
import {ShaderMaterial,WebGLRenderTarget,NearestFilter,RGBFormat} from 'three';
import Props from '../general/Props';

/**
 * 实时渲染材质
 */
export default class RenderMaterial extends ShaderMaterial{
    constructor(values={}){
        super();
        this._values=values;
        this.init();
    }

    //------------------------------------对外属性及方法--------------------------

    set camera(camera){
        this._camera=camera;
    }
    get camera(){
        return this._camera;
    }
    set scene(scene){
        this._scene=scene;
    }
    get scene(){
        return this._scene;
    }
    get width(){
        return this._props.width.value;
    }
    set width(num){
        this._props.width.value=num;
    }
    get height(){
        return this._props.height.value;
    }
    set height(num){
        this._props.height.value=num;
    }
    render(renderer){
        let prepare=(this._camera!==null)&&(this._scene!==null);
        if(prepare){
            renderer.setRenderTarget(this._buffer);
            renderer.clear();
            renderer.render(this._scene,this._camera);
            renderer.setRenderTarget(null);
        }
    }
    dispose(){
        super.dispose();
        this._camera=null;
        this._scene=null;
        this._buffer.dispose();
        this._values=null;
    }

    //----------------------------------私有属性及方法------------------------------

    init(){
        this.initProps();
        this.initVariable();
        this.initComponents();
        this.initPropCallback();
    }
    initProps(){
        this._props=new Props({
            width:this._values.width||512,
            height:this._values.height||512,
        });
    }
    initPropCallback(){
        this._props.onMultiKeyChange(()=>{
            let res=Math.max(this._props.width.value,this._props.height.value);
            this._buffer.setSize(res,res);
            this.uniforms.aspect.value=this._props.height.value/this._props.width.value;
        },'width','height');
    }
    initVariable(){
        this._camera=null;
        this._scene=null;
        this._buffer=null;
    }
    initComponents(){
        this.initBuffer();
        this.initVertexShader();
        this.initFragmentShader();
        this.initUniforms();
    }
    initBuffer(){
        let res=Math.max(this._props.width.value,this._props.height.value);
        this._buffer=new WebGLRenderTarget(res,res);
        this._buffer.texture.format=RGBFormat;
        this._buffer.texture.minFilter = NearestFilter;
        this._buffer.texture.magFilter = NearestFilter;
        this._buffer.texture.generateMipmaps = false;
        this._buffer.stencilBuffer = false;
    }
    initUniforms(){
        this.uniforms={
            map:{value:this._buffer.texture},
            aspect:{value:this._props.height.value/this._props.width.value}
        };
    }
    initVertexShader(){
        this.vertexShader=[
            'varying vec2 vuv;' ,
            'void main(){' ,
            '   vec4 mvPosition=modelViewMatrix*vec4(position,1.);' ,
            '   vuv=uv;' ,
            '   gl_Position=projectionMatrix*mvPosition;' ,
            '}'
        ].join('\n');
    }
    initFragmentShader(){
        this.fragmentShader=[
            'uniform sampler2D map;' ,
            'uniform float aspect;' ,
            'varying vec2 vuv;' ,
            'void main(){' ,
            '   vec2 tuv=vec2(vuv.xy)-vec2(0.5);' ,
            '   if(aspect>1.){' ,
            '      tuv.x/=aspect;' ,
            '   }else{' ,
            '      tuv.y*=aspect;' ,
            '   }' ,
            '   vec4 texCol=texture2D(map,tuv+vec2(0.5));' ,
            '   gl_FragColor=vec4(texCol.xyz,1.);' ,
            '}'
        ].join('\n');
    }
}